﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QuickGraph;
namespace GxcToCsharpParser.TreeWalkers.ControlFlowGraphBuilder
{
   public class ControlFlowGraphBuilder
   {
      private int branchLevel;
      AdjacencyGraph<ControlFlowGraphNode, TaggedEdge<ControlFlowGraphNode, int>> controlFlowGraph;
      private ControlFlowGraphNode currentNode;
      private int currentNodeIndex;
      private ControlFlowGraphNode previousNode;
      List<ControlFlowGraphNode> linearCfg;
      public ControlFlowGraphBuilder()
      {
         controlFlowGraph = null;
      }

      public AdjacencyGraph<ControlFlowGraphNode, TaggedEdge<ControlFlowGraphNode, int>> Build(List<ControlFlowGraphNode> programScope)
      {
         
         this.linearCfg = programScope;
         controlFlowGraph = new AdjacencyGraph<ControlFlowGraphNode, TaggedEdge<ControlFlowGraphNode, int>>();
         AddAllAstNodesToCfgVertices();
         JoinCfgVertices();
         return controlFlowGraph;
         
      }

      private void JoinCfgVertices()
      {
         previousNode = linearCfg[0];
         currentNodeIndex = 1;
         branchLevel = 0;
         int link = 1;
         while (currentNodeIndex < linearCfg.Count)
         {

            currentNode = linearCfg[currentNodeIndex];
            if (previousNode != null)
               ConnectCfgNodes(previousNode, currentNode, link);
            link = 1;
            switch (currentNode.Type)
            {
               case  ControlFlowNodeType.Else:
                  CreateCfgElseNode();
                  continue;
               case ControlFlowNodeType.If:
                  CreateCfgIfNode();
                  continue;
               case ControlFlowNodeType.Switch:
                  CreateCfgSwitchNode();
                  continue;
               case ControlFlowNodeType.Case:
                  CreateCfgCaseNode();
                  continue;
               case ControlFlowNodeType.DefaultCase:
                  CreateCfgDefaultCaseNode();
                  continue;
               case ControlFlowNodeType.Loop:
                  if (IsLoopEndNode())
                     link = 0;
                  break;
                case ControlFlowNodeType.DoLoop:
                  if (IsLoopEndNode())
                  {
                      link = 0;
                  }
                  break;
               case ControlFlowNodeType.Break:
                  if (IsCurrentNodeInALoop())
                  {
                     int i= GetEndOfThisLoop();
                     ConnectCfgNodes(currentNode, linearCfg[i + 1], 1);
                  }
                  currentNodeIndex++;
                  continue;
                case ControlFlowNodeType.Continue:
                  if (IsCurrentNodeInALoop())
                  {
                      int i = GetStartOfLoop();
                      ConnectCfgNodes(currentNode, linearCfg[i], 1);

                  }
                  currentNodeIndex++;
                  continue;

               
                  


            }
           
          
            previousNode = currentNode;
            currentNodeIndex++;
         }
      }

      private int GetStartOfDoLoop()
      {
          for (int i = currentNodeIndex - 1; i > -1; i--)
          {
              if (linearCfg[i].Type == ControlFlowNodeType.DoLoop)
                  return i;

          }
          return -1;
      }

private int GetStartOfLoop()
{
    for (int i = currentNodeIndex - 1; i > -1; i--)
    {
        if (linearCfg[i].Type == ControlFlowNodeType.Loop)
            return i;

    }
    return -1;
}

    

      private int GetEndOfThisLoop()
      {
         for (int i = currentNodeIndex; i < linearCfg.Count; i++)
         {
            if (linearCfg[i].Type == ControlFlowNodeType.Loop)
               return i;
            
         }
         return -1;
      }

      private bool IsCurrentNodeInALoop()
      {

         for (int i = currentNodeIndex - 1; i > -1; i--)
         {
            if (linearCfg[i].Type == ControlFlowNodeType.Loop)
               return true;
            else if (linearCfg[i].Type == ControlFlowNodeType.Case)
               return false;
         }

         return false;
      }

      private bool IsLoopEndNode()
      {
         var search = linearCfg[currentNodeIndex ];
         for (int i = currentNodeIndex - 1; i > -1; i--)
            if (search.HashCodeOfNode == linearCfg[i].HashCodeOfNode)
               return true;
         return false;
      }
      private void CreateCfgDefaultCaseNode()
      {
         int currentSwitchBranch = GetCurrentSwitch(currentNodeIndex);
         if (currentSwitchBranch != -1)
            currentSwitchBranch++;
         if (previousNode.Type != ControlFlowNodeType.Switch)
            ConnectCfgNodes(previousNode, linearCfg[currentSwitchBranch], 1);
         ControlFlowGraphNode temp2 = linearCfg[GetCurrentSwitch(currentNodeIndex)];
         var nextNode = linearCfg[currentNodeIndex + 1];
         if (nextNode.Type != ControlFlowNodeType.Case)
         {
            previousNode = nextNode;
            
            currentNodeIndex++;
         }
         currentNodeIndex++;
      }
      private void CreateCfgCaseNode()
      {
         int currentSwitchBranch = GetCurrentSwitch(currentNodeIndex);
         if (currentSwitchBranch != -1)
            currentSwitchBranch++;
         if(previousNode.Type != ControlFlowNodeType.Switch)
            ConnectCfgNodes(previousNode, linearCfg[currentSwitchBranch], 1);
         ControlFlowGraphNode temp2 = linearCfg[GetCurrentSwitch(currentNodeIndex)];
         var nextNode = linearCfg[currentNodeIndex + 1];
         if (nextNode.Type != ControlFlowNodeType.Case)
         {
            previousNode = nextNode;
            ConnectCfgNodes(temp2, nextNode, 0);
            currentNodeIndex++;
         }
         currentNodeIndex++;
      }

      private void CreateCfgSwitchNode()
      {
         //is there a default case 
         //target->join default case
         int defaultCaseIndex = GetDefaultCaseIfThisSwitchHasOne(currentNodeIndex+1);
         if (defaultCaseIndex !=-1)
         {
            var node = linearCfg[defaultCaseIndex+1];
            if(node.Type!= ControlFlowNodeType.BranchJoin)
               ConnectCfgNodes(currentNode, node, 1);
           
         }
         previousNode = currentNode;
         currentNodeIndex += 2;
      }

      private void CreateCfgIfNode()
      {
         var branchFalseIndex = GetIfFalseNode(currentNodeIndex + 1);
         var branchFalse = linearCfg[branchFalseIndex];

         ConnectCfgNodes(currentNode, branchFalse, 0);
         var branchTrueIndex = GetIfTrueNode(currentNodeIndex + 2);
         var branchTrue = linearCfg[branchTrueIndex];
         ConnectCfgNodes(currentNode, branchTrue, 1);
         currentNodeIndex = branchTrueIndex;
         previousNode = null;
         branchLevel++;
      }

      private void CreateCfgElseNode()
      {

         currentNode = GetBranchJoinNodeBelowCurrentNode(currentNodeIndex + 1);
         var temp = GetValidNodeAboveThisNode(currentNodeIndex - 1, branchLevel);
         var temp2 = GetBranchNode(currentNodeIndex - 1);
         ConnectCfgNodes(temp, currentNode, 1);
         previousNode = null;
         currentNodeIndex++;
         branchLevel--;
      }

      private int GetDefaultCaseIfThisSwitchHasOne(int nodeIndex)
      {
         for (int i = nodeIndex; i < linearCfg.Count; i++)
         {
            var temp = linearCfg[i];
            if (temp.Type == ControlFlowNodeType.Switch)
               return -1;
            else if (temp.Type == ControlFlowNodeType.DefaultCase)
               return i;

         }
         return -1;
      }

      private int GetCurrentSwitch(int nodeIndex)
      {
         for (int i = nodeIndex; i > -1; i--)
         {
            var temp = linearCfg[i];
            if (temp.Type == ControlFlowNodeType.Switch)
               return i;

         }
         return -1;
      }


      private ControlFlowGraphNode GetValidNodeAboveThisNode(int nodeIndex, int branchLevel)
      {
         for (int i = nodeIndex; i > -1; i--)
         {
            var temp = linearCfg[i];
            if (temp.Type != ControlFlowNodeType.BranchJoin)
               return temp;
            else if (branchLevel != 0)
               return temp;
         }
         return null;
      }

      private ControlFlowGraphNode GetNonBranchJoinNodeAboveThisNode(int nodeIndex)
      {
         for (int i = nodeIndex; i > -1; i--)
         {
            var temp = linearCfg[i];
            if (temp.Type != ControlFlowNodeType.BranchJoin)
               return temp;
         }
         return null;
      }

      private ControlFlowGraphNode GetBranchNode(int nodeIndex)
      {
         for (int i = nodeIndex; i > -1; i--)
         {
            var temp = linearCfg[i];
            if (temp.Type == ControlFlowNodeType.If)
               return temp;
         }
         return null;
      }

      private ControlFlowGraphNode GetBranchJoinNodeBelowCurrentNode(int nodeIndex)
      {
         int countBranches = 0;
         for (int i = nodeIndex; i < linearCfg.Count; i++)
         {
            var temp = linearCfg[i];
            switch (temp.Type)
            {
               case ControlFlowNodeType.If:
                  countBranches++;
                  break;
               case ControlFlowNodeType.Else:
                  if (countBranches > 0)
                     countBranches++;
                  break;
               case ControlFlowNodeType.BranchJoin:
                  if (countBranches == 0)
                     return temp;
                  countBranches--;
                  break;



            }
         }

         return null;
      }

      /// <summary>
      /// Gets if true node.
      /// </summary>
      /// <param name="trueBranchNodeIndex">Index of the true branch node.</param>
      /// <returns></returns>
      private int GetIfTrueNode(int trueBranchNodeIndex)
      {

         for (int i = trueBranchNodeIndex; i < linearCfg.Count; i++)
         {
            if (linearCfg[i].Type != ControlFlowNodeType.BranchJoin)
               return i;
         }
         return -1;
      }

      /// <summary>
      /// Gets if false node.
      /// </summary>
      /// <param name="falseBranchNodeIndex">Index of the false branch node.</param>
      /// <returns></returns>
      private int GetIfFalseNode(int falseBranchNodeIndex)
      {
         var node = linearCfg[falseBranchNodeIndex];
         int countIf = 0;
         int i = falseBranchNodeIndex + 1;
         for (; i < linearCfg.Count; i++)
         {
            var temp = linearCfg[i];
            if (temp.Type == ControlFlowNodeType.If)
               countIf++;
            else if (temp.Type == ControlFlowNodeType.Else)
            {

               if (countIf == 0)
               {
                  i++;
                  return i; ;
               }
               countIf--;
            }
            else if (temp.HashCodeOfNode == node.HashCodeOfNode && i != falseBranchNodeIndex + 1)
            {
               if (linearCfg[i + 1].Type != ControlFlowNodeType.Else)
                  return i;
            }

         }
         return -1;

      }




      private void AddAllAstNodesToCfgVertices()
      {

         foreach (var node in linearCfg)
         {

            if (IsNodeTypeACfgVertice(node.Type))
               controlFlowGraph.AddVertex(node);
         }
      }


      private bool IsNodeTypeACfgVertice(ControlFlowNodeType nodeType)
      {
         if (nodeType == ControlFlowNodeType.Else)
            return false;
         else if (nodeType == ControlFlowNodeType.Case)
            return false;
         else if (nodeType == ControlFlowNodeType.DefaultCase)
            return false;

         return true;
      }
      



      /// <summary>
      /// Adds the CFG node to the last vertice.
      /// </summary>
      /// <param name="node">The node.</param>
      private void AddCfgNode(ControlFlowGraphNode node)
      {
         ControlFlowGraphNode nodePrevious = controlFlowGraph.Vertices.ElementAt(controlFlowGraph.Vertices.Count() - 1);

         controlFlowGraph.AddVertex(node);
         TaggedEdge<ControlFlowGraphNode, int> edge = new TaggedEdge<ControlFlowGraphNode, int>(nodePrevious, node, 1);
         controlFlowGraph.AddEdge(edge);

      }
      /// <summary>
      /// Connects the CFG nodes.
      /// </summary>
      /// <param name="source">The source.</param>
      /// <param name="target">The target.</param>
      /// <param name="node">The node.</param>
      private void ConnectCfgNodes(ControlFlowGraphNode source, ControlFlowGraphNode target, int node)
      {
         if (controlFlowGraph.Vertices.Contains(source) && controlFlowGraph.Vertices.Contains(target))
         {
            TaggedEdge<ControlFlowGraphNode, int> edge = new TaggedEdge<ControlFlowGraphNode, int>(source, target, node);
            controlFlowGraph.AddEdge(edge);
         }
      }
  
     
   }
}
